package ai.neuralnet;


import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

/**
 * abstract Neural Network builder
 *
 * @param <N>  type of Neuron object to build
 * @param <NN> type of NeuralNetwork object to build
 */
public abstract class NNBuilder<N extends TNeuron, NN extends NeuralNetwork<N>>
{


    protected int inputCount;

    protected int outputCount;

    private Class<N> nClass;
    private Class<NN> nnClass;

    private Constructor<N> nCtor;
    private Constructor<NN> nnCtor;

    protected NNBuilder(Class<N> nClass, Class<NN> nnClass)
    {
        this.nClass = nClass;
        this.nnClass = nnClass;


        try
        {
            nCtor = nClass.getConstructor(int.class);
            nnCtor = nnClass.getConstructor(NNBuilder.class);
        } catch (NoSuchMethodException e)
        {
            e.printStackTrace();
        }

    }

    public abstract NN build();

    protected N makeNeuron(int inputSize)
    {

        N obj = null;
        try
        {
            obj = nCtor.newInstance(inputSize);
        } catch (InstantiationException e)
        {
            e.printStackTrace();
        } catch (IllegalAccessException e)
        {
            e.printStackTrace();
        } catch (InvocationTargetException e)
        {
            e.printStackTrace();
        }

        return obj;
    }

    protected N[] makeNeuronArray(int size)
    {
        return (N[]) Array.newInstance(nClass, size);
    }

    protected NN makeNeuralNetwork()
    {
        NN obj = null;
        try
        {
            obj = nnCtor.newInstance(this);
        } catch (InstantiationException e)
        {
            e.printStackTrace();
        } catch (IllegalAccessException e)
        {
            e.printStackTrace();
        } catch (InvocationTargetException e)
        {
            e.printStackTrace();
        }

        return obj;
    }


    public void setInputCount(int inputCount)
    {
        this.inputCount = inputCount;
    }

    public void setOutputCount(int outputCount)
    {
        this.outputCount = outputCount;
    }
}
